#include "vm_func.hpp"
#include "exception.hpp"
#include "value.hpp"
#include <sstream>
#include <cmath>
#include <iostream>

namespace BMD
{
  using std::ostringstream;
  using std::istringstream;
  using std::cout;
  using std::cerr;

  /** throws an argument error if the number of arguments passed
   * not match the expected number */
  void verify_argument_count(unsigned int num_args, unsigned int num_expected_args)
  {
    if (num_args < num_expected_args)
      throw argument_count_error(num_args, num_expected_args);
  }
  
  //! uses the normal print_visitor class
  Value to_string(vector<Value>& args)
  {
    verify_argument_count(args.size(), 1);

    ostringstream oss;
    oss << args[0];
    return Value(oss.str());
  }

  Value parse_int(vector<Value>& args)
  {
    verify_argument_count(args.size(), 1);

    int i;
    istringstream iss(args[0].get_string());
    iss >> i;

    return Value(i);
  }

  Value parse_float(vector<Value>& args)
  {
    verify_argument_count(args.size(), 1);

    double d;
    istringstream iss(args[0].get_string());
    iss >> d;

    return Value(d);
  }

  Value value_length(vector<Value>& args)
  {
    verify_argument_count(args.size(), 1);

    return Value((int)boost::apply_visitor(length_visitor(), args[0]));
  }

  Value string_length(vector<Value>& args)
  {
    verify_argument_count(args.size(), 1);
    
    // make sure we are using a string
    if (!args[0].is_type<string>())
      throw type_error("expects a string");
    
    return value_length(args);
  }
  
  Value array_length(vector<Value>& args)
  {
    verify_argument_count(args.size(), 1);
    
    // make sure we are using a string
    if (!args[0].is_type<BlockRef>() || 
        (args[0].get_block()->get_tag() != Block::Array))
      throw type_error("expects an array");
    
    return value_length(args);
  }
  
  Value random_float(vector<Value>& args)
  {
    return Value(double(rand())/(double)RAND_MAX);
  }

  Value print(vector<Value>& args, ostream& o)
  {
    verify_argument_count(args.size(), 1);

    Value print_val = args[0];

    // print to the specified output stream
    boost::apply_visitor(output_visitor(o), print_val);
    
    return Value();
  }
  
  Value print(vector<Value>& args) 
  {
    return print(args, cout); 
  }

  Value println(vector<Value>& args)
  {
    Value ret = print(args, cout);
    cout << endl;
    return ret;
  }

  Value print_error(vector<Value>& args)
  {
    return print(args, cerr);
  }
}
